#include "fs.h"
#include "file.h"
#include "fs_file.h"
#include "syscall.h"
#include "functions.h"
#include "console.h"
#include <string.h>
#include "frontend.h"
#include "proc.h"
#include "elf.h"
#include "config.h"
#include "mmap.h"
#include "riscv.h"
#include "proc.h"
#include "fat32.h"
elf_info current_elf_msg;


// Load a program segment into pagetable at virtual address va.
// va must be page-aligned
// and the pages from va to va+sz must already be mapped.
// Returns 0 on success, -1 on failure.
static int
loadseg( pte_t * pagetable, uint64 va, file_t* file, uint offset, uint sz)
{
  uint i, n;
  uint64 pa;

  if((va % PGSIZE) != 0)
    panic("loadseg: va must be page aligned");

  for(i = 0; i < sz; i += PGSIZE){

    pte_t * pte_ptr = walk_user_pagetable(pagetable, va + i,0);
    uint64 pa =PTE2PA(*pte_ptr);

    if(pa == 0)
      panic("loadseg: address should exist");
    if(sz - i < PGSIZE)
      n = sz - i;
    else
      n = PGSIZE;
    if(file_pread(file,  (void *)pa, n,offset+i) != n)
      return -1;

    if(DEBUG_ELF|DEBUG_EXEC){
      printk("pa %p va  %p  ; sz %p   n %p ; \n",pa ,i+va,sz,n);
      // uvmprint(pagetable);
    }

  }

  return 0;
}
static int 
do_loadseg(pagetable_t pagetable,uint64 va,struct dirent *ip, uint offset, uint sz)
{
	uint i,n;
	uint64 pa;
	if((va % PGSIZE) != 0)
		panic("loadseg: va must be page aligned");
	for(i = 0; i < sz; i += PGSIZE){
		pte_t *pte_ptr = walk_user_pagetable(pagetable,va +i ,0);
		uint64 pa = PTE2PA(*pte_ptr);
		if(pa == 0)
			panic("loadseg: address should exist");
		if(sz -i < PGSIZE)
			n = sz - i;
		else n = PGSIZE;
		if(eread(ip,0,(uint64)pa,offset+i,n) != n) return -1;
	}
	return 0;
}

typedef union {
  uint64 buf[MAX_ARGS];
  char* argv[MAX_ARGS];
} arg_buf;

void load_elf_to_proc(elf_info* current_elf_msg,struct proc * p)
{
  if(DEBUG_ELF){
    printk("load_elf_to_proc   %16lx\n",(uintptr_t)p->pagetable);
  }
  pte_t* pagetable = p->pagetable;
  //open ELF文件
  file_t* file = file_open(current_elf_msg->file_name, O_RDONLY, 0);
  if (IS_ERR_VALUE(file))
    goto fail;

  //读取elf header,并校验
  Elf_Ehdr eh;
  ssize_t ehdr_size = file_pread(file, &eh, sizeof(eh), 0);
  if (ehdr_size < (ssize_t)sizeof(eh) ||
      !(eh.e_ident[0] == '\177' && eh.e_ident[1] == 'E' &&
        eh.e_ident[2] == 'L'    && eh.e_ident[3] == 'F'))
    goto fail;
  kassert(IS_ELF64(eh));

  //读取program header
  size_t phdr_size = eh.e_phnum * sizeof(Elf_Phdr);
  if (phdr_size > current_elf_msg->phdr_size)
    goto fail;
  ssize_t ret = file_pread(file, (void*)current_elf_msg->phdr, phdr_size, eh.e_phoff);
  if (ret < (ssize_t)phdr_size)
    goto fail;
  current_elf_msg->phnum = eh.e_phnum;
  current_elf_msg->phent = sizeof(Elf_Phdr); //?phent?

  Elf_Phdr* ph_ptr = (typeof(ph_ptr))current_elf_msg->phdr;
  //  计算 ELF 中的最大虚拟地址
  uintptr_t max_vaddr = 0;
  for (int i = 0; i < eh.e_phnum; i++)
    if (ph_ptr[i].p_type == PT_LOAD && ph_ptr[i].p_memsz)
      max_vaddr = MAX(max_vaddr, ph_ptr[i].p_vaddr + ph_ptr[i].p_memsz);
  max_vaddr = ROUNDUP(max_vaddr, RISCV_PGSIZE);
  //为p->sz赋值
  p->sz=max_vaddr;

  //初始化入口地址
  current_elf_msg->entry = eh.e_entry;
  p->trapframe->epc=eh.e_entry;

  // Load program into memory.
  uint64 sz = 0;
  Elf_Phdr ph;
  for(int i=0, off=eh.e_phoff; i< eh.e_phnum ; i++, off+=sizeof(ph)){

    if(file_pread(file,(void *)&ph, sizeof(ph),off) != sizeof(ph)){
       printk("file_pread in load_elf_to_proc failed\n");
      goto fail;
    }

    if(ph.p_type != PT_LOAD){
      printk("ph.p_type != PT_LOAD\n");
      goto fail;
    }
    if(ph.p_memsz < ph.p_filesz){
       printk("ph.p_memsz < ph.p_filesz\n");
      goto fail;
    }
    if(ph.p_vaddr + ph.p_memsz < ph.p_vaddr){
       printk("ph.p_vaddr + ph.p_memsz < ph.p_vaddr\n");
      goto fail;
    }

    uint64 sz1;
    if((sz1 = uvmalloc(pagetable, sz, ph.p_vaddr + ph.p_memsz)) == 0){
       printk("uvmalloc == 0 \n ");
      goto fail;
    }

    // if(DEBUG_ELF){
    //   uvmprint(pagetable);
    //   printk("szold  %p  ; sznew %p  ;  ph.vaddr : %p\n",sz,ph.p_vaddr + ph.p_memsz,ph.p_vaddr);

    // }
    if(DEBUG_ELF){
      printk(" ---------------------------7  ph.p_vaddr %p\n",ph.p_vaddr);
    }

    sz = sz1;
    uint64 p_vaddr=ph.p_vaddr;
    uint64 offset=0;
    uint64 loadsz=ph.p_filesz;
    if(ph.p_vaddr % PGSIZE != 0){
      if(DEBUG_ELF){
       printk("ph.p_vaddr != ALIGNED sz %p\n",ph.p_filesz);
      }
       offset=ph.p_vaddr % PGSIZE;
       p_vaddr=PGROUNDDOWN(ph.p_vaddr);
       loadsz=offset+ph.p_filesz;
      // goto fail;
    }

    // if(DEBUG_ELF){
    //   printk(" ---------------------------8 \n");
    // }
    if(loadseg(pagetable, p_vaddr, file, ph.p_offset-offset, loadsz) < 0){
      printk("loadseg < 0 \n");
      goto fail;
    }

  //  if(DEBUG_ELF){
  //     printk(" ---------------------------9 \n");
  //  }

  }



  file_decref(file);
  return;

  fail:
    panic("couldn't open ELF program: %s!", current_elf_msg->file_name);
}




static size_t parse_args(arg_buf* arg_bug_msg)
{
  long r = frontend_syscall(SYS_getmainvars, va2pa(arg_bug_msg), sizeof(*arg_bug_msg), 0, 0, 0, 0, 0);
  kassert(r == 0);

  uint64 * pk_argv = &arg_bug_msg->buf[1];
  // argv[0] is the thinos itself.  skip it and any flags.
  size_t pk_argc = arg_bug_msg->buf[0], arg = 1;
//   for ( ; arg < argc && *(char*)(uintptr_t)argv[arg] == '-'; arg++)
//     handle_option((const char*)(uintptr_t)argv[arg]);

  for (size_t i = 0; arg + i < pk_argc; i++)
    arg_bug_msg->argv[i] = (char*)(uintptr_t)pk_argv[arg + i];
  return pk_argc - arg;
}

void init_elf_msg_by_argv0(elf_info * current_elf_msg,struct proc *p){
      //从命令行中获取app的名字
      //arg_bug_msg.argv[0]为app的带路径name例如:app/elf/app1
      arg_buf arg_bug_msg;
      size_t argc = parse_args(&arg_bug_msg);
      if (!argc)
          panic("tell me what ELF to load!");
      printk("app : %s\n", arg_bug_msg.argv[0]);


      //init elf_info,phdr初始为一个长度128的数组
      memcpy(current_elf_msg->file_name,arg_bug_msg.argv[0],sizeof(current_elf_msg->file_name));
      long phdrs[128];
      current_elf_msg->phdr = (uintptr_t)phdrs;
      current_elf_msg->phdr_size = sizeof(phdrs);

      if(DEBUG_ELF){
        printk("init_elf_msg_by_argv0\n");
      }
      load_elf_to_proc(current_elf_msg,p);

}


int
exec(char *path, char **argv)
{
  char *s, *last;
  int i, off;
  uint64 argc, sz = 0, sp, ustack[MAXARG+1], stackbase;
  Elf_Phdr ph;
  pagetable_t pagetable = 0, oldpagetable;
  struct proc *p = myproc();

  //open ELF文件
  file_t* file = file_open(path, O_RDONLY, 0);

  if (IS_ERR_VALUE(file))
    goto bad;

  //读取elf header,并校验
  Elf_Ehdr eh;
  ssize_t ehdr_size = file_pread(file, &eh, sizeof(eh), 0);
  if (ehdr_size < (ssize_t)sizeof(eh) ||
      !(eh.e_ident[0] == '\177' && eh.e_ident[1] == 'E' &&
        eh.e_ident[2] == 'L'    && eh.e_ident[3] == 'F'))
    goto bad;
  kassert(IS_ELF64(eh));

  if((pagetable = proc_pagetable(p)) == 0)
    goto bad;

  // Load program into memory.
  for(int i=0, off=eh.e_phoff; i< eh.e_phnum ; i++, off+=sizeof(ph)){
    if(file_pread(file,(void *)&ph, sizeof(ph),off) != sizeof(ph)){
       printk("file_pread in load_elf_to_proc failed\n");
      goto bad;
    }
    if(ph.p_type != PT_LOAD){
      printk("ph.p_type != PT_LOAD\n");
      goto bad;
    }
    if(ph.p_memsz < ph.p_filesz){
       printk("ph.p_memsz < ph.p_filesz\n");
      goto bad;
    }
    if(ph.p_vaddr + ph.p_memsz < ph.p_vaddr){
       printk("ph.p_vaddr + ph.p_memsz < ph.p_vaddr\n");
      goto bad;
    }
    uint64 sz1;
    if((sz1 = uvmalloc(pagetable, sz, ph.p_vaddr + ph.p_memsz)) == 0){
       printk("uvmalloc == 0 \n ");
       goto bad;
    }


    if(DEBUG_ELF){
      uvmprint(pagetable);
      printk("szold  %p  ; sznew %p  ;  ph.vaddr : %p\n",sz,ph.p_vaddr + ph.p_memsz,ph.p_vaddr);

    }
    if(DEBUG_EXEC){
      printk(" ---------------------------7  ph.p_vaddr %p\n",ph.p_vaddr);
    }
    sz = sz1;
    uint64 p_vaddr=ph.p_vaddr;
    uint64 offset=0;
    uint64 loadsz=ph.p_filesz;
    if(ph.p_vaddr % PGSIZE != 0){
      if(DEBUG_EXEC){
       printk("ph.p_vaddr != ALIGNED sz %p\n",ph.p_filesz);
      }
       offset=ph.p_vaddr % PGSIZE;
       p_vaddr=PGROUNDDOWN(ph.p_vaddr);
       loadsz=offset+ph.p_filesz;
    }

    if(DEBUG_EXEC){
      printk(" ---------------------------8 \n");
    }
    if(loadseg(pagetable, p_vaddr, file, ph.p_offset-offset, loadsz) < 0){
      printk("loadseg < 0 \n");
      goto bad;
    }


   if(DEBUG_EXEC){
      printk(" ---------------------------9 \n");
   }
  }

  p = myproc();
  uint64 oldsz = p->sz;

  sp = USER_STACK;
  stackbase = sp - PGSIZE;
 if(DEBUG_EXEC){
      printk(" -----------------argv %p---------- \n",argv);
   }

  // Push argument strings, prepare rest of stack in ustack.
  for(argc = 0; argv[argc]; argc++) {
    if(argc >= MAXARG)
      goto bad;
    if(DEBUG_EXEC){
       printk(" ----------------debug1-------%p--- \n",argv[0]);
       extern pte_t* root_page_table;
       uvmprint(p->pagetable);
    }


    sp -= strlen(argv[argc]) + 1;
    if(DEBUG_EXEC){
       printk(" ----------------debug2---------- \n",argv);
    }

    sp -= sp % 16; // riscv sp must be 16-byte aligned

    if(sp < stackbase)
      goto bad;
    if(copyout(pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
      goto bad;

    ustack[argc] = sp;
    // printk("ustack %d sp %p\n",argc,ustack[argc]);
  }
  ustack[argc] = 0;


  if(DEBUG){
      printk(" -------------sizeof(uint64) %p--------------10 \n",sizeof(uint64));
   }
  // push the array of argv[] pointers.
  // sp -= sizeof(uint64);
  sp -= (argc+1) * sizeof(uint64);
  sp -= sp % 16;
  if(sp < stackbase)
    goto bad;
  if(copyout(pagetable, sp, (char *)ustack, (argc+1)*sizeof(uint64)) < 0)
    goto bad;
//  printk("ustack  sp %p size %p\n",sp,(argc+1)*sizeof(uint64));
  // arguments to user main(argc, argv)
  // argc is returned via the system call return
  // value, which goes in a0.
  p->trapframe->a1 = sp;

  // printk("ustack[0] %p sp  %p\n",ustack[0],sp);
  //argc
  sp -= sizeof(uint64);
  // printk(" argc sp  %p\n",sp);
  if(sp < stackbase)
    goto bad;
  if(copyout(pagetable,sp, (char *)&argc, sizeof(uint64)) < 0)
      goto bad;

  if(DEBUG_EXEC){
      printk(" ---------------------------11 \n");
   }


  // Save program name for debugging.
  for(last=s=path; *s; s++)
    if(*s == '/')
      last = s+1;
  safestrcpy(p->name, last, sizeof(p->name));

  // Commit to the user image.
  struct trapframe * oldtrapframe=p->trapframe;
  oldpagetable = p->pagetable;
  p->pagetable = pagetable;
  p->sz = sz;
  p->trapframe->epc = eh.e_entry;  // initial program counter = main
  p->trapframe->sp = sp; // initial stack pointer
  proc_freepagetable(oldpagetable, oldsz,(uint64)oldtrapframe);

  return argc; // this ends up in a0, the first argument to main(argc, argv)

 bad:
      panic("couldn't open ELF program: %s!", path);

  return -1;
}
int
do_exec(char *path, char **argv){
	char *s,*last;
	int i,off;
	uint64 atgc, sz = 0, sp, ustack[MAXARG+1], stackbase;
	Elf_Ehdr elf;
	struct dirent *ip;
	Elf_Phdr ph;
	pagetable_t pagetable = 0, oldpagetable;
	struct proc *p = myproc();
	uint64 argc;
	if((ip = ename(path)) == 0) return -1;
	elock(ip);

	if(eread(ip,0,(uint64)&elf,0,sizeof(elf)) != sizeof(elf)) goto bad;
//	if(elf.e_ident[0] != '\177' || elf.e_ident[1] != 'E' || elf.e_ident[2] != 'L' || elf.e_ident[3] != 'F') goto bad;
//	if(elf.magic != ELF_MAGIC) goto bad;
	if((pagetable = proc_pagetable(p)) == 0) goto bad;

	// Load program into memory.
	for(i = 0,off = elf.e_phoff; i <elf.e_phnum; i++,off+=sizeof(ph)){
		if(eread(ip,0,(uint64)&ph,off,sizeof(ph)) != sizeof(ph)) goto bad;
		if(ph.p_type != PT_LOAD) continue;
		if(ph.p_memsz < ph.p_filesz)  goto bad;
		if(ph.p_vaddr + ph.p_memsz < ph.p_vaddr) goto bad;
		uint64 sz1;
		if((sz1 = uvmalloc(pagetable,sz,ph.p_vaddr + ph.p_memsz))== 0) goto bad;
		sz = sz1;
		uint64 p_vaddr = ph.p_vaddr;
		uint64 offset = 0;
		uint64 loadsz = ph.p_filesz;
		if(ph.p_vaddr % PGSIZE != 0){
			offset = ph.p_vaddr % PGSIZE;
			p_vaddr = PGROUNDDOWN(ph.p_vaddr);
			loadsz = offset + ph.p_filesz;
		}
		if(do_loadseg(pagetable,p_vaddr,ip,ph.p_offset-offset,loadsz) < 0) goto bad;
	}
	eunlock(ip);
	ip = 0;
	p = myproc();
	uint64 oldsz = p->sz;

	sp = USER_STACK;
	stackbase = sp - PGSIZE;

	//Push argument strings,prepare rest of stack in ustack.
	for(argc = 0; argv[argc]; argc++){
		if(argc >= MAXARG) goto bad;
		sp -= strlen(argv[argc]) + 1;
		sp -= sp % 16;
		if(sp < stackbase) goto bad;
		if(copyout(pagetable,sp,argv[argc],strlen(argv[argc]) + 1) < 0) goto bad;
		
		ustack[argc] = sp;
	}
	ustack[argc] = 0;

	// push the array of argv[] pointers.
	sp -= (argc + 1) * sizeof(uint64);
	sp -= sp % 16;
	if(sp < stackbase) goto bad;
	if(copyout(pagetable,sp,(char *)ustack,(argc+1)*sizeof(uint64)) < 0) goto bad;
	sp -= sizeof(uint64);
	if(sp < stackbase) goto bad;
	if(copyout(pagetable,sp,(char *)&argc,sizeof(uint64)) < 0) goto bad;

	// Save Program name for debugging.
	for(last = s = path;*s;s++)
		if(*s == '/') last = s + 1;
	safestrcpy(p->name,last,sizeof(p->name));

	//commit to the user image.
	struct trapframe * oldtrapframe = p->trapframe;
	oldpagetable = p->pagetable;
	p->pagetable = pagetable;
	p->sz = sz;
	p->trapframe->epc = elf.e_entry;
	p->trapframe->sp = sp;
	proc_freepagetable(oldpagetable,oldsz,(uint64)oldtrapframe);

	return argc;
bad:
	panic("couldn't open ELF  program: %s",path);
	if(ip) eunlock(ip);
	return -1;
}
